import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class StackQueuePanel extends JPanel {
	
	private ArrayList<LabeledRectangle> theStack;
	private ArrayList<LabeledRectangle> theQueue;
	
	private JPanel CenterPanel, WorkPanel,HelpPanel;	
	
	private JButton enqueueButton, 
					dequeueButton,
					resetQueueButton,
					resetStackButton,
					stackPushButton, 
					stackPopButton;
	
	private JLabel ErrorLabel;
	
	private final int MAX_QUEUE_SIZE = 10;
	private final int MAX_STACK_SIZE = 10;
	private final int QUEUE_START_X = 10;
	private final int QUEUE_START_Y = 165;
	private final int STACK_START_Y = 35;
	private final int STACK_START_X = 10;
	private final int NODE_WIDTH = 75;
	private final int NODE_HEIGHT = 75;
	private final int NODE_SPACER = 10;
	
	private final Color NODE_COLOR = new Color(0x00746b);
	
	// create the stack and queue screen using a border layout.
	// the center panel also uses a border layout so that the help panel does not
	// extend to the right side of the screen
	public StackQueuePanel()
	{

		theStack = new ArrayList<LabeledRectangle>();
		theQueue = new ArrayList<LabeledRectangle>();
		
		WorkPanel = new JPanel();
		HelpPanel = new JPanel();
		CenterPanel = new JPanel();
				
		this.setBackground(Color.white);
		WorkPanel.setBackground(Color.white);
		
		HelpPanel.setPreferredSize(new Dimension(0, 100));
		HelpPanel.setLayout(new BoxLayout(HelpPanel, BoxLayout.Y_AXIS));
		
		JLabel HelpLabel = new JLabel("Stacks and Queues can be implemented with an Array or Linked List");
		
		HelpLabel.setFont(new Font("Dialog", 1, 14));
		HelpLabel.setForeground(Color.black);
		HelpLabel.setAlignmentX(CENTER_ALIGNMENT);
		
		JLabel HelpLabel1 = new JLabel("In a Stack, nodes are added and removed from the end of the structure");
		
		HelpLabel1.setFont(new Font("Dialog", 1, 14));
		HelpLabel1.setForeground(Color.black);
		HelpLabel1.setAlignmentX(CENTER_ALIGNMENT);
		
		JLabel HelpLabel2 = new JLabel("In a Queue, nodes are added to the beginning of the structure but removed from the end");
		
		HelpLabel2.setFont(new Font("Dialog", 1, 14));
		HelpLabel2.setForeground(Color.black);
		HelpLabel2.setAlignmentX(CENTER_ALIGNMENT);
		
		
		ErrorLabel = new JLabel();
		
		ErrorLabel.setFont(new Font("Dialog", 1, 14));
		ErrorLabel.setForeground(Color.red);
		ErrorLabel.setAlignmentX(CENTER_ALIGNMENT);
		
		HelpPanel.add(HelpLabel);
		HelpPanel.add(HelpLabel1);
		HelpPanel.add(HelpLabel2);
		HelpPanel.add(Box.createRigidArea(new Dimension(0,10)));
		HelpPanel.add(ErrorLabel);
		
		createStack(MAX_STACK_SIZE);
		createQueue(MAX_QUEUE_SIZE);
		
		BorderLayout blayout2 = new BorderLayout();
		blayout2.setHgap(3);
		blayout2.setVgap(3);
		
		CenterPanel.setLayout(blayout2);
		CenterPanel.add(WorkPanel, BorderLayout.CENTER);
		CenterPanel.add(HelpPanel, BorderLayout.SOUTH);
		
		BorderLayout blayout = new BorderLayout();
		blayout.setHgap(3);
		blayout.setVgap(3);
		
		this.setLayout(blayout);
		this.add(CenterPanel, BorderLayout.CENTER);
		this.add(ControlPanel(), BorderLayout.EAST);
		
	}
	
	public JPanel ControlPanel() {
		
		JPanel thisPanel = new JPanel();
		thisPanel.setPreferredSize(new Dimension(200, 0));
		thisPanel.setLayout(new BoxLayout(thisPanel, BoxLayout.Y_AXIS));
		
		JLabel stackLabel = new JLabel("Stack Operations");
		JLabel queueLabel = new JLabel("Queue Operations");
		
		stackLabel.setFont(new Font("Dialog", 1, 14));
		queueLabel.setFont(new Font("Dialog", 1, 14));
		
		enqueueButton = new JButton("Enqueue Node");
		dequeueButton = new JButton("Dequeue Node");
		stackPushButton = new JButton("Push Node");
		stackPopButton = new JButton("Pop Node");
		resetStackButton = new JButton("Reset");
		resetQueueButton = new JButton("Reset");
		
		// add action listeners to the buttons
		ActionEventHandler aeh = new ActionEventHandler();
		enqueueButton.addActionListener(aeh);
		dequeueButton.addActionListener(aeh);
		stackPushButton.addActionListener(aeh);
		stackPopButton.addActionListener(aeh);
		resetQueueButton.addActionListener(aeh);
		resetStackButton.addActionListener(aeh);
		
		stackLabel.setAlignmentX(CENTER_ALIGNMENT);
		queueLabel.setAlignmentX(CENTER_ALIGNMENT);
		enqueueButton.setAlignmentX(CENTER_ALIGNMENT);
		dequeueButton.setAlignmentX(CENTER_ALIGNMENT);
		stackPushButton.setAlignmentX(CENTER_ALIGNMENT);
		stackPopButton.setAlignmentX(CENTER_ALIGNMENT);
		resetStackButton.setAlignmentX(CENTER_ALIGNMENT);
		resetQueueButton.setAlignmentX(CENTER_ALIGNMENT);

		thisPanel.add(Box.createRigidArea(new Dimension(0,10)));
		thisPanel.add(stackLabel);
		thisPanel.add(Box.createRigidArea(new Dimension(0,10)));
		thisPanel.add(stackPushButton);
		thisPanel.add(stackPopButton);
		thisPanel.add(resetStackButton);
		
		thisPanel.add(Box.createRigidArea(new Dimension(0,10)));
		thisPanel.add(queueLabel);
		thisPanel.add(Box.createRigidArea(new Dimension(0,10)));
		thisPanel.add(enqueueButton);
		thisPanel.add(dequeueButton);
		thisPanel.add(resetQueueButton);
		
		return thisPanel;
	}
	
	public void createStack(int size)
	{
		int startX = STACK_START_X;
		// add new nodes to the stack
		for (int i = 0; i < size; i++)
		{				
			theStack.add(new LabeledRectangle(startX, STACK_START_Y, NODE_WIDTH, NODE_HEIGHT, NODE_COLOR, "" + i + ""));
			startX += NODE_WIDTH + NODE_SPACER;	
		}	
	}
	
	public void createQueue(int size)
	{
		int startX = QUEUE_START_X;
		// add new nodes to the stack
		for (int i = 0; i < size; i++)
		{
			theQueue.add(new LabeledRectangle(startX, QUEUE_START_Y, NODE_WIDTH, NODE_HEIGHT, NODE_COLOR, "" + i + ""));
			startX += NODE_WIDTH + NODE_SPACER;	
		}	
		
	}

	public void paint(Graphics g) {
			
		super.paint(g);

		//g.setColor(Color.black);
		g.setFont(new Font("Dialog", Font.PLAIN, 18));
		g.drawString("Stack (" + theStack.size() + "/" + MAX_STACK_SIZE + ")", STACK_START_X, STACK_START_Y - 10);
		g.drawString("Queue (" + theQueue.size() + "/" + MAX_QUEUE_SIZE + ")", QUEUE_START_X, QUEUE_START_Y - 10);
		
		// draw the stack and queue nodes to the panel
		for (int i = 0; i < theStack.size(); i++)
		{
			theStack.get(i).draw(g);
		}
		
		for (int i = 0; i < theQueue.size(); i++)
		{
			theQueue.get(i).draw(g);
		}
		
	}

	private class ActionEventHandler implements ActionListener {

		public void actionPerformed(ActionEvent e) {

			ErrorLabel.setText(""); // reset the error label when a button is pressed
			
			if (e.getSource() == enqueueButton)
			{
				
				new Thread(new Runnable() {  
				    public void run() { 
				    					
						if (theQueue.size() < MAX_QUEUE_SIZE) // can't enqueue if it is full
						{
							
							int newX;
							if (theQueue.isEmpty())
							{
								newX = QUEUE_START_X;
							}
							else
								newX = theQueue.get(theQueue.size() -1).x + NODE_SPACER + NODE_WIDTH;
							
							LabeledRectangle lr = new LabeledRectangle(newX, QUEUE_START_Y, NODE_WIDTH, NODE_HEIGHT, Color.red, "" + (theQueue.size()+1) + ""); 
												
							theQueue.add(lr);
														
							repaint();
							
							dequeueButton.setEnabled(false);
							enqueueButton.setEnabled(false);
							resetQueueButton.setEnabled(false);
							
							
							try {
								Thread.sleep(500);
							} catch (InterruptedException e1) {
								e1.printStackTrace();
							}
							
							dequeueButton.setEnabled(true);
							enqueueButton.setEnabled(true);
							resetQueueButton.setEnabled(true);
							
							lr.setColor(NODE_COLOR);
							
							repaint();
							
						}
						else
							ErrorLabel.setText("The queue is full. Remove some items to enqueue.");
							
				    }
				}).start();
				
			}
			else if (e.getSource() == dequeueButton)
			{
				
				new Thread(new Runnable() {  
				    public void run() { 
				    					
						if (!theQueue.isEmpty())
						{			
							
							LabeledRectangle lr = theQueue.get(0);
							
							lr.setColor(Color.red);
							
							repaint();
							
							dequeueButton.setEnabled(false);
							enqueueButton.setEnabled(false);
							resetQueueButton.setEnabled(false);
							
							try {
								Thread.sleep(500);
							} catch (InterruptedException e1) {
								e1.printStackTrace();
							}
							
							dequeueButton.setEnabled(true);
							enqueueButton.setEnabled(true);
							resetQueueButton.setEnabled(true);
							
							theQueue.remove(lr); // remove the item at the top

							for (int i = 0; i < theQueue.size(); i++)
							{						
								theQueue.get(i).x -= NODE_WIDTH + NODE_SPACER;
							}
							
							repaint();
						}
						else
							ErrorLabel.setText("The queue is empty. Add some items to dequeue.");
							
				    }
				}).start();
				
				
			}
			else if (e.getSource() == stackPushButton)
			{
				new Thread(new Runnable() {  
				    public void run() { 
				    	
						if (theStack.size() < MAX_STACK_SIZE) // cant push onto a full stack
						{
											
							int newX;
							
							if (theStack.isEmpty())
								newX = STACK_START_X;
							else
								newX = theStack.get(theStack.size() - 1).x + NODE_WIDTH + NODE_SPACER;		
							
							LabeledRectangle lr = new LabeledRectangle(newX, STACK_START_Y, NODE_WIDTH, NODE_HEIGHT, Color.red, "" + (theStack.size()+1) + "");
							
							theStack.add(lr);
											
							repaint();
							
							stackPushButton.setEnabled(false);
							stackPopButton.setEnabled(false);
							resetStackButton.setEnabled(false);
							
							try {
								Thread.sleep(500);
							} catch (InterruptedException e1) {
								e1.printStackTrace();
							}
							
							stackPushButton.setEnabled(true);
							stackPopButton.setEnabled(true);
							resetStackButton.setEnabled(true);
							
							lr.setColor(NODE_COLOR);
							
							repaint();
							
						}
						else
							ErrorLabel.setText("The stack is full. Remove some items before using Push.");
				    }
				}).start();
				

			}
			else if (e.getSource() == stackPopButton)
			{
				
				new Thread(new Runnable() {  
				    public void run() { 
				    	
						if (!theStack.isEmpty())
						{			
							
							LabeledRectangle lr = theStack.get(theStack.size() - 1);
							
							lr.setColor(Color.red);
							
							repaint();
							
							stackPushButton.setEnabled(false);
							stackPopButton.setEnabled(false);
							resetStackButton.setEnabled(false);
							
							try {
								Thread.sleep(500);
							} catch (InterruptedException e1) {
								e1.printStackTrace();
							}
							
							stackPushButton.setEnabled(true);
							stackPopButton.setEnabled(true);
							resetStackButton.setEnabled(true);
							
							theStack.remove(lr); // remove the item at the top
							
							repaint();
						}
						else
							ErrorLabel.setText("The stack is empty. Add items before using Pop.");
				    }
				}).start();

				
			}
			else if (e.getSource() == resetQueueButton)
			{
				theQueue.clear();
				createQueue(MAX_QUEUE_SIZE);
				repaint();
			}
			else if (e.getSource() == resetStackButton)
			{
				theStack.clear();
				createStack(MAX_STACK_SIZE);
				repaint();
			}
			
		}
		
	}

}


